#include "TdsReqHandler.h"
#include "tds/BasicReqHandler.h"
#include "tds/TFBasicInfoHandler.h"
#include "tds/TFSimilarBondHandler.h"
#include "tds/TFBondBbo.h"
#include "tds/TFBondBboPushHandler.h"
#include "tds/TFKLineReqHandler.h"
#include "tds/TFKLinePushHandler.h"
#include "tds/TFSettlementHandler.h"
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include "qbtools/tools.h"
#include "tfbondlib/BondFuture.h"
#include <QMetaEnum>

TdsReqHandler::TdsReqHandler()
{
}

TdsReqHandler::~TdsReqHandler()
{
    for (auto iter = m_cmdHandlers.begin(); iter != m_cmdHandlers.end(); ++iter) {
        delete iter->second;
        iter->second = nullptr;
    }
}

bool /*is_my_duty*/ TdsReqHandler::webHandlerFunc(int browserId, qint64 frameId, const QString& category, const QJsonValue& req, int& error, QString& result)
{
    QMetaEnum metaColor = QMetaEnum::fromType<TdsCmdHandler::TDS_OPERATE_TYPE>();
    bool isOk = false;
    const int icategory = metaColor.keyToValue(category.toStdString().c_str(), &isOk);
    if (!isOk) {
        return false;
    }

	QJsonObject reqObj = req.toArray()[0].toObject();
    QString callback = QByteArray::fromBase64(reqObj["callback"].toString().toUtf8());
	QString cmdContent = QByteArray::fromBase64(reqObj["req"].toString().toUtf8());

	QJsonParseError err;
	QJsonDocument doc = QJsonDocument::fromJson(cmdContent.toUtf8(), &err);
	QJsonObject cmdObj = doc.object();
    if (cmdObj.isEmpty()) {
        error = 1001;
        result = "Failed to parse \"req\" content!";
        return true;
	}

    log_warning("TdsReqHandler::webHandlerFunc: category=%s, req=%s, callback=%s", category.toStdString().c_str(), cmdContent.toStdString().c_str(), callback.toStdString().c_str());

    QString cmd = cmdObj["type"].toString();
    ReqInfo reqinfo{callback, cmdContent, frameId, browserId};

    switch (static_cast<TdsCmdHandler::TDS_OPERATE_TYPE>(icategory)) {
    case TdsCmdHandler::TDS_OPERATE_TYPE::tds_unreg: {
        std::lock_guard<std::mutex> locker(m_lock);
        auto iter = m_cmdHandlers.find(cmd);
        if (iter != m_cmdHandlers.end()) {
            // TODO: need to do more to stop the data pusher
            // dtor will disconnect the signal & slot to stop the data pusher
            delete iter->second;
            iter->second = nullptr;
            m_cmdHandlers.erase(iter);
        }
    } break;

    case TdsCmdHandler::TDS_OPERATE_TYPE::tds_reg: {
        std::lock_guard<std::mutex> locker(m_lock);
        auto iter = m_cmdHandlers.find(cmd);
        if (iter != m_cmdHandlers.end()) {
            error = 1002;
            result = QString("[%1] is already Registered!").arg(cmd);
            return true;
        }

        TdsCmdHandler* cmdHanlder = TdsCmdHandler::cmdHandler(cmd);
        if (cmdHanlder) {
            m_cmdHandlers[cmd] = cmdHanlder;
            QObject::connect(cmdHanlder, &TdsCmdHandler::sendDataToWeb, [this](qint64 frame_id, int browserId, const QString& res, const QString& cb){
                this->onSendDataToWeb(browserId, frame_id, res, cb);
            });
            cmdHanlder->processCmd(cmdObj, reqinfo, false);
            // TODO: need to do more to start the data pusher
            // ctor will connect the signal & slot to start the data pusher
        } else {
            error = 1003;
            result = QString("Failed to Create TdsCmdHandler for [%1]!").arg(cmd);
            return true;
        }
    } break;
    case TdsCmdHandler::TDS_OPERATE_TYPE::tds_req: {
        TdsCmdHandler* cmdHanlder = TdsCmdHandler::cmdHandler(cmd);
        if (cmdHanlder) {
            QObject::connect(cmdHanlder, &TdsCmdHandler::sendDataToWeb, [this, cmdHanlder](qint64 frame_id, int browserId, const QString& res, const QString& cb){
                this->onSendDataToWeb(browserId, frame_id, res, cb);
                cmdHanlder->deleteLater();
            });
            cmdHanlder->processCmd(cmdObj, reqinfo, false);
        } else {
            error = 1003;
            result = QString("Failed to Create TdsCmdHandler for [%1]!").arg(cmd);
            return true;
        }
        // NOTE: cmdHanlder created in tds_req case, will not be stored in m_cmdHandlers
    } break;
    default:
        break;
    }

    result = "[]";
    return true;
}

void TdsReqHandler::onSendDataToWeb(int browserId, qint64 frame_id, const QString &res, const QString &cb)
{
    log_warning("TdsReqHandler::onSendDataToWeb: res=%s, cb=%s", res.toStdString().c_str(), cb.toStdString().c_str());
}

#define META_OBJ(CLASS) CLASS::staticMetaObject
TdsCmdHandler* TdsCmdHandler::cmdHandler(const QString& cmd)
{
	static std::map<QString, QMetaObject>	s_instances;
	if (s_instances.empty())
	{
		s_instances["tds.req.system.language.info"] = META_OBJ(BasicReqHandler);
		s_instances["tds.req.tf.basic.info"] = META_OBJ(TFBasicInfoHandler);
		s_instances["tds.req.irb.simi.bond"] = META_OBJ(TfSimilarBondHandler);
		s_instances["tds.req.bond.bbo"] = META_OBJ(BondBboHandler);
		s_instances["tds.push.bond.bbo"] = META_OBJ(BondBboPushHandler);
		s_instances["tds.req.tf.kline"] = META_OBJ(TFKLineReqHandler);
		s_instances["tds.push.tf.kline"] = META_OBJ(TFKLinePushHandler);
		s_instances["tds.req.tf.settlement"] = META_OBJ(TFSettlementHandler);

        //added by czp
        //s_instances["tds.signal"] = META_OBJ(TFSettlementHandler);
        s_instances["tds.push.system.state"] = s_instances["tds.req.system.language.info"];
        //s_instances["tds.push.bond.deal"] = META_OBJ(TFSettlementHandler);
        //s_instances["tds.req.system.attention"] = META_OBJ(TFSettlementHandler);
	}

	auto iter = s_instances.find(cmd);
	if (iter == s_instances.end())
		return nullptr;

	QMetaObject obj = iter->second;
	return dynamic_cast<TdsCmdHandler*>(obj.newInstance());
}


QString displayPrice(const PRICE* varPrice)
{
	QString strRet = "";
	QString strSemiQuote = "";
	QString strColHitFlag = "";

	bool bValid = (strcmp(varPrice->m_price_status, "0") == 0 ? false : true);

	if (strcmp(varPrice->m_description, "") != 0)
	{
		strRet = varPrice->m_description;
	}

	int nType = SSTools::getSemiQuote(varPrice->m_rebate, varPrice->m_return_point, varPrice->m_sPrice, strSemiQuote);

	QString strTemp;
	if (nType == -1)
	{
		if (strcmp(varPrice->m_sPrice, "") != 0 && bValid)
		{
			strTemp = SSTools::FormatePrice(varPrice->m_sPrice);
			strRet = strTemp;
		}
		else
		{
			strRet = "--";
		}
	}
	else if (nType == 0)
	{
		strRet = strSemiQuote;
	}
	else if (nType == 1)
	{
		if (strcmp(varPrice->m_sPrice, "") != 0 && strcmp(varPrice->m_sPrice, "Bid") != 0)
		{
			strTemp = SSTools::FormatePrice(varPrice->m_sPrice);
			strRet = strTemp;
		}
		else
		{
			strRet = "";
		}
	}

	return strRet;
}

const char* getTfKey(const char* pId)
{
	const char* pKey = nullptr;
	CFutureArray& FutureArray = CBondFuture::instance().GetFutureArray();
	for (auto iter = FutureArray.m_data.begin(); iter != FutureArray.m_data.end(); ++iter)
	{
		if (iter->IsEqualTo(pId))
		{
			pKey = iter->GetFutureKey();
			break;
		}
	}

	return pKey;
}

